package com.fingard.dsp.bank.directbank.gzdsf01;

import com.fingard.constant.Format;
import com.fingard.diagnostics.LogHelper;
import com.fingard.dsp.bank.directbank.DirectBase;
import com.fingard.dsp.bank.directbank.FailCode;
import com.fingard.dsp.bank.directbank.gzdsf01.util.CfbsUtils;
import com.fingard.net.ConnState;
import com.fingard.net.ITcpClient;
import com.fingard.net.TcpReceiver;
import com.fingard.text.StringHelper;
import com.fingard.util.TransIDHelper;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Date;

public class GZDSF01Base extends DirectBase implements ITcpClient {
	public String getCharset() {
		return getBankConfig().getCharset("GBK");
	}
	
	private static TransIDHelper idHelper = new TransIDHelper(1, 9999);

	/**16位*/
    protected String getQryTransID() {
        Date tmpDateNow = new Date();
        return Format.DateTime12Year2Format.format(tmpDateNow) + idHelper.getNextIDStr();
    }
	
    protected String getEncryptKey() {
    	String encryptKey = getBankConfig().getValueAsString("encryptKey");
		WriteBankLogLn("通讯密钥内容:" + encryptKey);
		return encryptKey;
    }
    
	protected int getBatSeqNo(String p_reqDir) {
		int batSeqNo = -1;
		File batSeqNoFileDir = new File(p_reqDir);
		if (batSeqNoFileDir.exists()) {
			File[] batSeqNoFileList = batSeqNoFileDir.listFiles();
			if (batSeqNoFileList != null) {
				batSeqNo = batSeqNoFileList.length + 1;
			} else {
				batSeqNo = 1;
			}
		} else {
			batSeqNo = 1;
		}
		
		return batSeqNo;
	}
	
	/**
     * 实时交易登录
     * 
     * 业务场景：  
     * 		1、机构在发起业务前，需向代收付中心进行登录。
     * 		2、登录成功后，在签退成功前，可持续正常收发交易，不需每天或每次发起业务前都进行登录/签退操作;
     * 		2、机构登录/签退只用于实时业务控制;
     */
    protected String[] login() {
    	String[] tmpStrRet = new String[] { "", "" };
        try {
        	synchronized(bankFront){//登录采用同步
        		if (StringHelper.isNullOrEmpty(bankFront.loginFlag)) {
            		WriteBankLogLn("当前未登录前置机，现在进行登录...");
            		
            		StringBuilder tmpLoginBuilder = new StringBuilder(); 
            		tmpLoginBuilder.append("900101");// 交易种类
            		tmpLoginBuilder.append("1202");// 报文类型
            		String transid = getQryTransID();
            		tmpLoginBuilder.append(transid);// 交易序号
            		String waitKey = "9001011212"+transid;
            		tmpLoginBuilder.append(Format.Date8Format.format(new Date()));// 委托日期
            		tmpLoginBuilder.append(String.format("%-12s", bankFront.org));// 发起方代码（收费机构代码）
            		tmpLoginBuilder.append(String.format("%-12s",bankFront.serverCode));// 接收方代码（代收付中心行号）"906581000018"
            		if(StringHelper.hasAnyChar(bankFront.token)){
            			tmpLoginBuilder.append(String.format("%-20s", bankFront.token));// 登录标识/旧登录标识
            		}else{
            			tmpLoginBuilder.append(String.format("%-20s", bankFront.corpNo));// 登录标识/旧登录标识
            		}
            		tmpLoginBuilder.append(String.format("%-20s", ""));// 新登录标识
            		tmpLoginBuilder.append(String.format("%-8s", ""));// 返回码
            		tmpLoginBuilder.append(String.format("%-64s", ""));// 校验码
            		
            		String plainTxt = tmpLoginBuilder.toString();
            		
            		WriteBankLogLn("登录请求生成摘要前数据明文:\r\n" + plainTxt);
            		
            		String tmpLoginRequest = new CfbsUtils(this.getSbLog()).txnEncSample(plainTxt, getEncryptKey(), getCharset());
            		if (StringHelper.isNullOrEmpty(tmpLoginRequest)) {
            			WriteBankLogLn("前置机登录报文加密失败!");
            			tmpStrRet[0] = ConnState.FAIL;
            			tmpStrRet[1] = "前置机登录报文加密失败!";
            			return tmpStrRet;
            		}
            		bankFront.lastLoginTime = new Date();
            		tmpStrRet = sendToBankBySocket(tmpLoginRequest);
                    WriteBankLogLn("登录响应状态标识:" + tmpStrRet[1]);
            		WriteBankLogLn("登录响应报文:" + tmpStrRet[1]);
            		if(StringHelper.isNullOrEmpty(tmpStrRet[0])){
            			int waitSecs = getReadTimeoutSec("login", 15);//15秒
            			WriteBankLogLn("waitOne("+waitKey+")");
            			this.bankFront.resetEvent.waitOne(waitKey, 1, waitSecs);
            			if("true".equals(bankFront.loginFlag)){
            				WriteBankLogLn("登录成功，登录标识："+bankFront.token);
            			}else{
            				WriteBankLogLn("登录尚未成功");
            			}
            		}
            	}
        	}
        } catch (Exception ex) {
        	tmpStrRet[0] = ConnState.FAIL;
        	tmpStrRet[1] = LogHelper.getStackTraceFirstLine(ex);
        	this.WriteBankLogLn("前置机登录异常：", ex);
        }
        
        return tmpStrRet;
    }
    
    /**加密后发送
     * @throws Exception */
    protected String[] encryptAndSend(String pSendMsg) throws Exception{
    	WriteBankLogLn("明文数据:" + pSendMsg);
		
		String reqMsg = new CfbsUtils(this.getSbLog()).txnEncSample(pSendMsg, getEncryptKey(), getCharset());
		if(StringHelper.hasAnyChar(reqMsg)){
			return sendToBankBySocket(reqMsg);
		}else{
			
			return new String[] { FailCode.E9981, "生成的密文为空" };
		}
    }
    
    /**
     * 数据发送银行
     * 
     * @param tmpReqData
     * @return
     * @throws Exception
     */
    protected String[] sendToBank(String tmpReqData) throws Exception {
    	String[] tmpStrRet = new String[] { "", "" };
    	
    	
    	//System.out.println("登录前登录标志:" + bankFront.loginFlag);
    	if (StringHelper.isNullOrEmpty(bankFront.loginFlag)) {
    		
    		String[] logRet = login();
    		WriteBankLogLn("发送登录请求后-bankFront.loginFlag:" + bankFront.loginFlag) ;
    		if (StringHelper.isNullOrEmpty(bankFront.loginFlag)) {
    			tmpStrRet[0] = ConnState.FAIL;
        		tmpStrRet[1] = "自动登录失败："+logRet[1];
                return tmpStrRet;
    		}
    	}
    	
    	sendToBankBySocket(tmpReqData);
    	
    	return tmpStrRet;
    }
    

    /**
     * socket发送
     * 
     * @param tmpReqData
     * @return
     * @throws Exception
     */
    protected String[] sendToBankBySocket(String tmpReqData) throws Exception {
    	String[] tmpStrRet = new String[] { "", "" };
		byte[] tmpBytesHead = new byte[] {(byte) 0xef,(byte) 0x14};
		byte[] tmpBytesBody = tmpReqData.getBytes(getCharset());
		byte[] tmpBytesSend = new byte[tmpBytesHead.length + tmpBytesBody.length];
		System.arraycopy(tmpBytesHead, 0, tmpBytesSend, 0, tmpBytesHead.length);
		System.arraycopy(tmpBytesBody, 0, tmpBytesSend, tmpBytesHead.length, tmpBytesBody.length);
		//System.out.println("发送数据:\r\n" + new String(tmpBytesSend));
		WriteBankLogStep2("发送数据：" + new String(tmpBytesSend));
		WriteBankUrlLogLn("提交地址：" + bankFront.serverIP + ":" + bankFront.serverPort);
		int timeoutMil = this.getReadTimeout(15);
		Socket socket = null;
		String objKey = "socket";
		if(!bankFront.customObjects.containsKey(objKey)){
			synchronized(bankFront.customObjects){
				if(!bankFront.customObjects.containsKey(objKey)){
					try{
						socket = new Socket(bankFront.serverIP, Integer.parseInt(bankFront.serverPort));
						socket.setSoTimeout(timeoutMil);//180000
						bankFront.customObjects.put(objKey, socket);
					}catch(Exception ex){
						this.WriteBankLogLn(ex);
						tmpStrRet[0] = ConnState.FAIL;
			    		tmpStrRet[1] = LogHelper.getStackTraceFirstLine(ex);
			    		if(socket!=null){
			    			socket.close();
			    		}
			            return tmpStrRet;
					}
				}
			}
		}
		socket = (Socket)this.bankFront.customObjects.get(objKey);
		try {
			synchronized(socket){
				OutputStream tmpOutput = socket.getOutputStream();
				tmpOutput.write(tmpBytesSend);
			}
			this.bankFront.lastVisitTime = new Date();
		} catch (IOException e) {
			this.WriteBankLogLn(e);
			tmpStrRet[1] = LogHelper.getStackTraceFirstLine(e);
			synchronized(bankFront.customObjects){
				if(bankFront.customObjects.containsKey(objKey)){
					bankFront.customObjects.remove(objKey);
					bankFront.loginFlag = "";
					bankFront.lastLoginTime = null;
					bankFront.lastVisitTime = null;
				}
			}
			socket.close();
			throw(e);
		}finally{
			
		}
		return tmpStrRet;
	}
    
	public void getContentLength(TcpReceiver p_receiver) throws Exception {
		if (p_receiver.contentLength == -1) {
			if (p_receiver.readedLength >= 11) {
				String tmpHead = new String(p_receiver.recBytes, 3, 8, getCharset());
				p_receiver.contentLength = Integer.parseInt(tmpHead.trim());
				p_receiver.contentStartIndex = 2;
			}
		}
	}
}
